package com.gitee.xzb617.mybatis.sqllog.util;

import cn.hutool.core.date.DateUtil;
import com.manticore.jsqlformatter.JSQLFormatter;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.type.TypeHandlerRegistry;

import java.text.DateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;

/**
 * Sql工具
 * @author xuzhibin
 */
public class SqlUtil {

    /**
     * 格式化请求参数
     * @param boundSql
     * @return
     */
    public static String formatArgs(Configuration configuration, BoundSql boundSql) {
        Object parameterObject = boundSql.getParameterObject();
        if (parameterObject == null) {
            return "[=== 无 ===]";
        }
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        String args = "";
        if (hasTypeHandler(configuration, parameterObject.getClass())) {
            args = getArg(parameterObject);
        } else {
            // 对JavaBean、Collection、Map三种类型对象的操作
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            for (ParameterMapping parameterMapping : parameterMappings) {
                String propertyName = parameterMapping.getProperty();
                if (metaObject.hasGetter(propertyName)) {
                    Object obj = metaObject.getValue(propertyName);
                    args = args + getArg(obj) + ", ";
                }
                else if (boundSql.hasAdditionalParameter(propertyName)) {
                    Object obj = boundSql.getAdditionalParameter(propertyName);
                    args = args + getArg(obj) + ", ";
                } else {
                    args = args + "null, ";
                }
            }
        }
        if (args.endsWith(", ")) {
            args = args.substring(0, args.length()-2);
        }
        return args;
    }


    /**
     * 格式化sql，并填充参数
     * @param configuration
     * @param boundSql
     * @return
     */
    public static String formatSql(Configuration configuration, BoundSql boundSql, boolean format) {
        Object parameterObject = boundSql.getParameterObject();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        // 在sql需要多用一个空格隔开
        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
        if (parameterObject==null || CollectionUtils.isEmpty(parameterMappings)) {
            return format?formatSqlWithJava(sql):sql;
        }
        // 有参数的情况下，替换 ？
        if (hasTypeHandler(configuration, parameterObject.getClass())) {
            sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getArgValue(parameterObject)));
        } else {
            // 对JavaBean、Collection、Map三种类型对象的操作
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            for (ParameterMapping parameterMapping : parameterMappings) {
                String propertyName = parameterMapping.getProperty();
                if (metaObject.hasGetter(propertyName)) {
                    Object obj = metaObject.getValue(propertyName);
                    sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getArgValue(obj)));
                }
                else if (boundSql.hasAdditionalParameter(propertyName)) {
                    Object obj = boundSql.getAdditionalParameter(propertyName);
                    sql = sql.replaceFirst("\\?", Matcher.quoteReplacement(getArgValue(obj)));
                }
                else {
                    sql = sql.replaceFirst("\\?", "null");
                }
            }
        }
        return format?formatSqlWithJava(sql):sql;
    }


    private static String formatSqlWithJava(String originSql) {
        try {
            if (originSql.contains("--") || originSql.contains("<!--")) {
                return originSql;
            }
//            return SQLUtils.formatOracle(originSql);
            return JSQLFormatter.format(originSql);
        } catch (Exception e) {
            return "[sql格式化失败]";
        }
    }

    private static String getArgValue(Object obj) {
        if (obj == null) {
            return "";
        }
        if (obj instanceof String) {
            return toSqlStrArg(obj);
        }
        if (obj instanceof Date) {
            Date dateObj = (Date) obj;
            String formatDate = DateUtil.format(dateObj, DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA));
            return toSqlStrArg(formatDate);
        }
        return obj.toString();
    }

    private static String getArg(Object obj) {
        if (obj == null) {
            return "";
        }
        if (obj instanceof String) {
            return obj.toString() + "(String)";
        }
        if (obj instanceof Date) {
            Date dateObj = (Date) obj;
            String formatDate = DateUtil.format(dateObj, DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA));
            return formatDate + "(Date)";
        }
        return obj.toString() + "(" + obj.getClass().getTypeName() + ")";
    }


    private static String toSqlStrArg(Object obj) {
        return "'" + obj.toString() + "'";
    }

    private static boolean hasTypeHandler(Configuration configuration, Class<?> type) {
        TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        return typeHandlerRegistry.hasTypeHandler(type);
    }

}
